/*
 * Copyright 2008 CSC Consulting Group under the Apache License, 
 * Version 2.0 (the "License"); you may not use this file except in 
 * compliance with the License. You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 
 * law or agreed to in writing, software distributed under the License is 
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License.
 */
package com.waw.memberMap.plugin;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import com.jivesoftware.base.Group;
import com.jivesoftware.base.GroupManager;
import com.jivesoftware.base.GroupNotFoundException;
import com.jivesoftware.base.UnauthorizedException;
import com.jivesoftware.base.User;
import com.jivesoftware.base.Log;
import com.jivesoftware.community.Blog;
import com.jivesoftware.community.Community;
import com.jivesoftware.community.CommunityManager;
import com.jivesoftware.community.CommunityNotFoundException;
import com.jivesoftware.community.JiveGlobals;
import com.jivesoftware.community.JiveIterator;
import com.jivesoftware.community.ProfileManager;
import com.jivesoftware.community.UserProfile;
import com.jivesoftware.community.event.ProfileEvent;
import com.jivesoftware.community.event.ProfileListener;

/**
 * This class listens for changes to a User's Profile, calculates the geolocation of the user based on their 
 * address, and sets the following extended properties on the User:
 * <ul>
 * <li><strong>USER_MMAP_LON</strong> The latitude for the User's home address.
 * <li><strong>USER_MMAP_LON</strong> The longitude for the User's home address.
 * .
 * @see com.waw.memberMap.plugin.GeoCoder.
 *
 * @author Jim Tremlett
 */
public class UserProfileListener implements ProfileListener 
{
	/**
	 * This method is called when the User's profile is changed.
	 * The method calculates the geolocation of the user based on their address and
	 * set the user as a member of the selected well at work group.
	 *
	 * @see com.jivesoftware.community.event.ProfileListener#profileUpdated(com.jivesoftware.community.event.ProfileEvent)
	 */
	public void profileUpdated(ProfileEvent evt) 
	{
		User user = evt.getUser();
		Map<String, String> userProperties = user.getProperties();
		Log.debug(user.getName()+"'s profile has been modified.");
		
		// get the user's profile and retrieve the home address
		// get the profile field that holds the address
		String addrFieldStr = JiveGlobals.getJiveProperty(MemberMapConsts.GLOBAL_MMAP_ADDRESS);
		if(addrFieldStr != null)
		{
			long addrField = Long.parseLong(addrFieldStr);
			ProfileManager pManager = PluginUtils.getProfileManager();
			UserProfile addrPF =  pManager.getProfile(user).get(addrField);
			String address = null;
			if (addrPF != null) address= addrPF.getValue();
			 if(address != null && !address.equals(""))
			 {
				 try {
					Location location = GeoCoder.getLocation(address);
					userProperties.put(MemberMapConsts.USER_MMAP_LAT, location.getLat());
					userProperties.put(MemberMapConsts.USER_MMAP_LON, location.getLon());
					Log.debug(user.getName()+"'s geocode location set to ("+location.getLat()+", "+ location.getLon()+")");

				} catch (IOException e) {
						Log.warn("Problem geocoding "+user.getName()+"'s home address: "+ address, e);
				}
			 }
		}
		
		// set the user's membership in a well at work community
		String groupStr = JiveGlobals.getJiveProperty(MemberMapConsts.GLOBAL_MMAP_GROUP);
		if(groupStr != null)
		{
			long groupField = Long.parseLong(groupStr);
			ProfileManager pManager = PluginUtils.getProfileManager();
			UserProfile grpPF = pManager.getProfile(user).get(groupField);
			String group = null;
			if(grpPF != null)group = grpPF.getValue();
			 if(group != null && !group.equals(""))
			 {
				 setUserGroup(user, group);
			 }
		}
	}

	/**
	 * Sets the user's group membership to the named community.
	 * @param user The user to set the membership.
	 * @param group the Name of the community to have the User become the member.
	 */
	private void setUserGroup(User user, String group) {
		// Search the communities for one with the "group" as member.profile.name as an extended property.
		CommunityManager cMan = PluginUtils.getCommunityManager();
		ArrayList<Community> cList = PluginUtils.getListOfCommunities(cMan.getRootCommunity());
		Iterator<Community> cIter = cList.iterator();
	

		while(cIter.hasNext())
		{
			Community comm = cIter.next();
			String commProfileStr = comm.getProperties().get(MemberMapConsts.COMM_MMAP_PROFILE_NAME);
			if(commProfileStr != null && commProfileStr.length() > 0)
			{
				// Retrieve the member.group extended property from the community.
				// If the user's selected group matches the community's member.group, the the user's member group
				if(commProfileStr.equals(group)) changeUserCommunity(user,comm);
			}
		}
	}

	/**
	 * This method changes the community that a User is a member of.
	 *This involves two functions:
	 * <ul>
	 * <li>Adding/Moving the user from the general membership groups associated with the community
	 * <li>Adding/Moving the user from the blogs associated with the community.
	 * </ul>
	 * @param user  The User to change their group membership.
	 * @param comm The Community that the User should become a member.
	 */
	private void changeUserCommunity(User user,Community comm) {
		GroupManager gMan = PluginUtils.getGroupManager();
		CommunityManager cMan = PluginUtils.getCommunityManager();
		
		// get the user's community if that property is set
		String userCommStr = user.getProperties().get(MemberMapConsts.USER_MMAP_COMM);
		Community userComm = null;
		
				if(userCommStr != null)
					try 
				{
						userComm =  cMan.getCommunity(Long.parseLong(userCommStr));
					} catch (Exception e) 
					{
						Log.error("Unable to locate Community with ID: "+userCommStr);
					}
				if(userComm != null)
					{ 
						/* 
						 * User is already a member of another community. 
						 * We are going to brunt force here and remove the user from that community's membership group and blogs
						 */
				
						// remove the user from the membership group
						String commGroupStr = userComm.getProperties().get(MemberMapConsts.COMM_MMAP_GROUP);
						Group commGroup = null;
						if(commGroupStr != null)
							try {
								commGroup = gMan.getGroup(Long.parseLong(commGroupStr));
							} catch (GroupNotFoundException e) {
								Log.error("Unable to locate Group with ID: "+commGroupStr);
							}
						if(commGroup != null) commGroup.removeMember(user);
						
						// remove the user from the blogs with the community
						JiveIterator<Blog> oldBlogs = userComm.getBlogs();
						while(oldBlogs.hasNext())
						{
							try{oldBlogs.next().removeUser(user);}
							catch(Exception e)
							{
								Log.warn("Unable to remove user["+user.getName()+
											"' from blog associated with Community ["+userComm.getName()+"]");
							}
						}
					}
				
					/*
					 * Add the user to the community's membership group and the community's blogs
					 */
					// add the user from the membership group
					String commGroupStr = comm.getProperties().get(MemberMapConsts.COMM_MMAP_GROUP);
					Group commGroup = null;
					if(commGroupStr != null)
						try {
							commGroup = gMan.getGroup(Long.parseLong(commGroupStr));
						} catch (Exception e) {
							Log.error("Unable to locate Group with ID: "+commGroupStr);
						}
					if(commGroup != null) commGroup.addMember(user);
					
					// add the user to the blogs associated  with the community
					JiveIterator<Blog> newBlogs = comm.getBlogs();
					while(newBlogs.hasNext())
					{
						try{newBlogs.next().addUser(user);}
						catch(Exception e)
						{
							Log.warn("Unable to add user["+user.getName()+
										"' to blog associated with Community ["+userComm.getName()+"]");
						}
					}
					// record the user is now a member of the community
					user.getProperties().put(MemberMapConsts.USER_MMAP_COMM,Long.toString(comm.getID()));
			}
}
